using Assimp;
using OpenTK.Graphics.OpenGL4;
using System.Collections.Generic;
using System.Linq;

namespace _3Dto2D.Services;

public class MeshRenderer : IDisposable
{
    private readonly int _vao;
    private readonly int _vbo;
    private readonly int _ebo;
    private readonly int _indicesCount;

    public MeshRenderer(Mesh mesh)
    {
        _indicesCount = mesh.FaceCount * 3;

        _vao = GL.GenVertexArray();
        GL.BindVertexArray(_vao);

        _vbo = GL.GenBuffer();
        GL.BindBuffer(BufferTarget.ArrayBuffer, _vbo);

        var vertices = new List<float>();
        for (int i = 0; i < mesh.VertexCount; i++)
        {
            // Position
            vertices.Add(mesh.Vertices[i].X);
            vertices.Add(mesh.Vertices[i].Y);
            vertices.Add(mesh.Vertices[i].Z);

            // Normal
            if (mesh.HasNormals)
            {
                vertices.Add(mesh.Normals[i].X);
                vertices.Add(mesh.Normals[i].Y);
                vertices.Add(mesh.Normals[i].Z);
            }
            else
            {
                vertices.AddRange(new float[] { 0.0f, 0.0f, 0.0f });
            }

            // Texture Coordinates
            if (mesh.HasTextureCoords(0))
            {
                vertices.Add(mesh.TextureCoordinateChannels[0][i].X);
                vertices.Add(mesh.TextureCoordinateChannels[0][i].Y);
            }
            else
            {
                vertices.AddRange(new float[] { 0.0f, 0.0f });
            }
        }
        GL.BufferData(BufferTarget.ArrayBuffer, vertices.Count * sizeof(float), vertices.ToArray(), BufferUsageHint.StaticDraw);

        _ebo = GL.GenBuffer();
        GL.BindBuffer(BufferTarget.ElementArrayBuffer, _ebo);

        var indices = new List<uint>();
        foreach (var face in mesh.Faces)
        {
            indices.AddRange(face.Indices.Select(i => (uint)i));
        }
        GL.BufferData(BufferTarget.ElementArrayBuffer, indices.Count * sizeof(uint), indices.ToArray(), BufferUsageHint.StaticDraw);

        const int stride = (3 + 3 + 2) * sizeof(float);

        // Vertex positions
        GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, stride, 0);
        GL.EnableVertexAttribArray(0);

        // Vertex normals
        GL.VertexAttribPointer(1, 3, VertexAttribPointerType.Float, false, stride, 3 * sizeof(float));
        GL.EnableVertexAttribArray(1);

        // Texture coordinates
        GL.VertexAttribPointer(2, 2, VertexAttribPointerType.Float, false, stride, 6 * sizeof(float));
        GL.EnableVertexAttribArray(2);

        GL.BindVertexArray(0);
    }

    public void Render()
    {
        GL.BindVertexArray(_vao);
        GL.DrawElements(OpenTK.Graphics.OpenGL4.PrimitiveType.Triangles, _indicesCount, DrawElementsType.UnsignedInt, 0);
        GL.BindVertexArray(0);
    }

    public void Dispose()
    {
        GL.DeleteVertexArray(_vao);
        GL.DeleteBuffer(_vbo);
        GL.DeleteBuffer(_ebo);
    }
}
